Remix + Contentfulのチュートリアルが公開されたので実際に触りつつ、Netlifyにデプロイしてみる。
どうも、ベルリンオフィスの小西です。
ヘッドレスCMSのContentfulから、Remixのチュートリアルが公開されていたので試してみました。
今回は簡単なポートフォリオのウェブサイトを立ち上げてみます。
Remixって何?という方は弊社で記事がありますのでそちらをご参照ください↓
https://dev.classmethod.jp/articles/qucikstart-remix-framework/
1. Contentful側の準備
Contentful にアクセスし、アカウント作成 & 新規Space作成を終えたあと、
下記の情報をコピー&APIキーを作成しておきます。
- Contentful Space ID
- Delivery API Key … 公開された記事・アセットデータのみを取得する本番用API
- Content Management Token … 書き込み権限を持つ最上位のAPI
注意点
Content Management Token を利用することからも分かる通り、本スターターではContentfulのスペースにコンテンツモデルや記事の追加を行います。
例えば Blog
という被りそうな名称のコンテンツモデルを追加したりするため、既にContentfulでコンテンツを管理している方は、新規でspaceを立ち上げておいた方が無難です。Contentfulは無料プランで2つまでSpace作成ができます。
2. Remixアプリケーションの立ち上げ
ターミナルからContentfulのRemixスタックをクローンします。
% npx create-remix@latest --template https://github.com/contentful/starter-remix-portfolio
このスタックではデザインに Tailwind CSS、コンテンツの取得に Contentful GraphQL API を使用しています。
色々聞かれますので、順に入力していきます。
? Where would you like to create your app? ./contentful-sample ? TypeScript or JavaScript? JavaScript (どっちでも可) ? Do you want me to run `npm install`? Yes ? Enter you Contentful Space ID (先ほど作成したSpace ID) ? Enter you Content Delivery API Key (先ほど作成したDelivery API Key) ? Enter you Content Management Token (先ほど作成したManagement Token = `CFPAT-`から始まる文字列)
無事Contentfulへのアップロードとアプリケーション作成が完了したら、Content Management Tokenはもう使わないので削除しても問題ありません。
3. development サーバーを立ち上げる
作成が完了したら、development サーバーを立ち上げます。
% cd contentful-sample % npm run dev
localhost:3000にアクセスし、サイトが表示されていたらOKです。
4. スタックの解説
上記を試してみました、だけではアレなので少し解説をしてみます。
Contentfulへの記事の追加
ContentfulではCLIからリソースのエクスポート&インポートが可能で、設定をjsonファイルで記述します。
例えばContentfulにシードデータを流し込みたい時に便利で、扱えるリソースは記事だけではなく、画像などのアセット、コンテンツモデル、Webhook、Role情報などもエクスポート&インポートできます。
本スターターでは実行と同時にContentfulへのデータインポート実行を行いますが、その設定がファイルが下記にあたります。
https://github.com/contentful/starter-remix-portfolio/blob/main/remix.init/contentful/export.json
上記は % npx create-remix
した際に実行される初回処理(入力されたContentfulのトークンなどを .env
に保存したりなど)の一部に組み込まれています。
Contentfulのデータ取得
Contentfulは REST と GraphQL の2つのAPIエンドポイントを持っています。
本スターターでは下記のファイルでapiCall()関数としてGraphQLエンドポイントにPOSTリクエストを送っています。
app/models/contentful.server.js
async function apiCall(query, variables) { const fetchUrl = `https://graphql.contentful.com/content/v1/spaces/${SPACE}/environments/master`; const options = { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${TOKEN}`, }, body: JSON.stringify({ query, variables }), } return await fetch(fetchUrl, options) }
上記ファイルで各コンテンツモデルに対するクエリを一括して記述し、 client
変数でexportしてます。
export const client = {getProjects, getTalks, getAllBlogs, getSingleBlog, getPage}
app/root.jsx
app/root.jsx
はRemixにおけるページレンダリングの親(Remixいうところの"root route”)としてアプリケーション全体のレイアウトを行います。
Remixではディレクトリ/ファイル構成がそのままアプリケーションのURLになるため、例えば app/routes/about.jsx
の about()
の返り値は app/root.jsx
の Outlet
内でレンダリングされ、URL= /about
としてアクセスできます。
各コンポーネントの役割は下記のようになります。
app/root.jsx
export default function App() { return ( <html lang="en"> <head> <Meta />{/* app/routes/ 内の MetaFunction の返り値をタグとして出力。なお meta はRoutesにて予約語のためシンプルに変数 meta としてexportもできる */} <Links />{/* app/routes/ 内の LinksFunction の返り値をタグとして出力 */} </head> <body> <NavBar /> <main> <Outlet />{/* app/routes/ 内の各ページでexport defaultされた関数の返り値がレンダリングされる */} </main> <ScrollRestoration /> <Scripts />{/* スクリプトがここに入る */} <LiveReload />{/* コード変更時に自動リロードを行うかどうかを定義。developmentのみで動作 */} </body> </html> ); }
詳細は → https://remix.run/docs/en/v1/api/remix#links-livereload-meta-scripts-scrollrestoration
以上の通り、ここでは全体をラップするレイアウトを担当するのみで直接Contentfulデータをレンダリングしません。次を見てみます。
app/routes
配下のファイルたち
Remixでは app/routes/
配下に入ったファイルはRoutesとして、ファイル名をURLとしてそのままアクセスできるようになります。
例えばURL= /projects/
としてアクセスできる app/routes/projects.jsx
を開いてみると、先ほどの app/models/contentful.server.js
を通じて、Contentfulの projects
モデルの記事全てと、ページ名 "Projects" をキーにページメタデータ(SEOメタデータなど)をContentfulから取得しているのがわかります。
import { client } from "../models/contentful.server"; ... export async function loader() { const projects = await client.getProjects(); const page = await client.getPage("Projects") return json({projects, page}); }
なお、 loader()で取得したデータは useLoaderData
がJSONデータとして返すようになります。
またリッチテキストの出力には @contentful/rich-text-types
と @contentful/rich-text-react-renderer
を使っており、HTML変換時に自前の処理(HTMLタグの追加や置換)が可能です。GatsbyやNext.jsですでにContentfulに触っている人には馴染み深いと思います。
5. Netlifyにデプロイしてみる
Remix側でのデプロイ関連のファイルは下記2つになります。Remixではフロントエンドとバックエンドのコードを一箇所で記述できて楽ですね。
- remix.config.js
- server.js
(閑話休題)Remixプロジェクト作成時のデプロイ先選択について
Remixでは最初のプロジェクト作成時に選択したデプロイ先(eg. Netlify, Cloudflare Pages…)に従って上記サーバー関連ファイルの生成や関連パッケージのインストールを行います。
なお、本スターターはNetlify専用になっています。
(実はデプロイ先をNetlifyからCloduflare Pages用に変更しようとしたところ、Node.jsの基本ライブラリの置換やGraphQLへのPOSTリクエスト処理で詰まってしまい難儀なことになってしまったので、やはりRemixは最初からデプロイ先をある程度想定した開発が推奨なのだと思います)
Netlifyでプロジェクト作成
コードをリポジトリにPushiしたら、あとはNetlifyから新規プロジェクトを追加するのみです。
Netlifyでは自動的にRemixを認識してくれるため、基本はデフォルト設定のままで問題ありません。
Contentfulの環境変数のみ追加しておきます(ローカルに生成されている .env と同じもので大丈夫です)。
- CONTENTFUL_SPACE_ID
- CONTENTFUL_ACCESS_TOKEN
Netlifyにもデプロイできました! 非常に簡単でした。
最後に
以上、Remix + ContentfulのWebアプリケーションを立ち上げ、Netlifyにデプロイしてみました。
次回はRemixスターターのクラスメソッド版(with Cloudflare Pages)を作ってみようかと思っています。
参考情報
- https://www.contentful.com/remix-tutorial/
- Remix, Next.jsそれぞれの違いと利点が簡単にまとまっています(英語): https://bejamas.io/blog/remix-vs-nextjs/